package cc.shacocloud.mirage.utils.annotation;

import cc.shacocloud.mirage.utils.charSequence.StrJoiner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

/**
 * {@link LinkedHashMap} 子类，表示注释属性键值对
 */
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
    
    private static final String UNKNOWN = "unknown";
    
    @Nullable
    private final Class<? extends Annotation> annotationType;
    
    private final String displayName;
    
    private boolean validated = false;
    
    /**
     * 创建一个新的空 {@link AnnotationAttributes} 实例。
     */
    public AnnotationAttributes() {
        this.annotationType = null;
        this.displayName = UNKNOWN;
    }
    
    /**
     * 创建一个新的空 {@link AnnotationAttributes} 实例，该实例具有给定的初始容量以优化性能。
     *
     * @param initialCapacity 基础地图的初始大小
     */
    public AnnotationAttributes(int initialCapacity) {
        super(initialCapacity);
        this.annotationType = null;
        this.displayName = UNKNOWN;
    }
    
    /**
     * 创建一个新的 {@link AnnotationAttributes} 实例，包装提供的映射及其所有键值对。
     *
     * @see #fromMap(Map)
     */
    public AnnotationAttributes(Map<String, Object> map) {
        super(map);
        this.annotationType = null;
        this.displayName = UNKNOWN;
    }
    
    /**
     * 复制一个新的 {@link AnnotationAttributes} 实例
     *
     * @see #fromMap(Map)
     */
    public AnnotationAttributes(AnnotationAttributes other) {
        super(other);
        this.annotationType = other.annotationType;
        this.displayName = other.displayName;
        this.validated = other.validated;
    }
    
    /**
     * 为指定的 {@code annotationType} 创建一个新的空 {@link AnnotationAttributes} 实例。
     */
    public AnnotationAttributes(@NotNull Class<? extends Annotation> annotationType) {
        this.annotationType = annotationType;
        this.displayName = annotationType.getName();
    }
    
    /**
     * 为指定的 {@code annotationType} 创建一个可能已验证的新空 {@link AnnotationAttributes} 实例。
     */
    AnnotationAttributes(@NotNull Class<? extends Annotation> annotationType, boolean validated) {
        this.annotationType = annotationType;
        this.displayName = annotationType.getName();
        this.validated = validated;
    }
    
    /**
     * 返回一个基于给定映射的 {@link AnnotationAttributes} 实例
     */
    @Nullable
    public static AnnotationAttributes fromMap(@Nullable Map<String, Object> map) {
        if (map == null) {
            return null;
        }
        if (map instanceof AnnotationAttributes) {
            return (AnnotationAttributes) map;
        }
        return new AnnotationAttributes(map);
    }
    
    /**
     * 获取此 {@code AnnotationAttributes} 表示的注解类型。
     *
     * @return 注解类型，如果未知，则为 {@code null}
     */
    @Nullable
    public Class<? extends Annotation> annotationType() {
        return this.annotationType;
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值作为字符串
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    public String getString(@NotNull String attributeName) {
        return getRequiredAttribute(attributeName, String.class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值作为字符串数组
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    public String[] getStringArray(@NotNull String attributeName) {
        return getRequiredAttribute(attributeName, String[].class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值作为布尔值
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    public boolean getBoolean(@NotNull String attributeName) {
        return getRequiredAttribute(attributeName, Boolean.class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值作为数字
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    @SuppressWarnings("unchecked")
    public <N extends Number> N getNumber(@NotNull String attributeName) {
        return (N) getRequiredAttribute(attributeName, Number.class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值作为枚举
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    @SuppressWarnings("unchecked")
    public <E extends Enum<?>> E getEnum(@NotNull String attributeName) {
        return (E) getRequiredAttribute(attributeName, Enum.class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值作为类
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    @SuppressWarnings("unchecked")
    public <T> Class<? extends T> getClass(@NotNull String attributeName) {
        return getRequiredAttribute(attributeName, Class.class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值作为类数组
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    public Class<?>[] getClassArray(@NotNull String attributeName) {
        return getRequiredAttribute(attributeName, Class[].class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的 {@link AnnotationAttributes}。
     * <p>
     * 注意：如果你需要一个实际的注释，请调用{@link #getAnnotation(String, Class)}。
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    public AnnotationAttributes getAnnotation(@NotNull String attributeName) {
        return getRequiredAttribute(attributeName, AnnotationAttributes.class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的类型 {@code annotationType} 的注释。
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    public <A extends Annotation> A getAnnotation(@NotNull String attributeName, @NotNull Class<A> annotationType) {
        return getRequiredAttribute(attributeName, annotationType);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的 {@link AnnotationAttributes} 数组。
     * <p>
     * 如果存储在指定 {@code attributeName} 下的值是 {@code AnnotationAttributes} 的实例，则在返回之前，它将包装在单元素数组中。
     * <p>
     * 注意：如果你需要一个实际的注解数组，请调用{@link #getAnnotationArray(String, Class) }
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    public AnnotationAttributes[] getAnnotationArray(@NotNull String attributeName) {
        return getRequiredAttribute(attributeName, AnnotationAttributes[].class);
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的 {@code 注释类型} 的数组
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    @SuppressWarnings("unchecked")
    public <A extends Annotation> A[] getAnnotationArray(@NotNull String attributeName, @NotNull Class<A> annotationType) {
        Object array = Array.newInstance(annotationType, 0);
        return (A[]) getRequiredAttribute(attributeName, array.getClass());
    }
    
    /**
     * 获取存储在指定 {@code attributeName} 下的值，确保该值为 {@code 预期类型}
     * <p>
     * 如果 {@code expectType} 是一个数组，并且存储在指定 {@code attributeName} 下的值是预期数组类型的组件类型的单个元素，
     * 则单个元素将在返回之前包装在适当类型的单元素数组中。
     *
     * @throws IllegalArgumentException 如果属性不存在或不属于预期类型
     */
    @SuppressWarnings("unchecked")
    public <T> T getRequiredAttribute(@NotNull String attributeName, @NotNull Class<T> expectedType) {
        Object value = get(attributeName);
        assertAttributePresence(attributeName, value);
        assertNotException(attributeName, value);
        if (!expectedType.isInstance(value) && expectedType.isArray() &&
                expectedType.getComponentType().isInstance(value)) {
            Object array = Array.newInstance(expectedType.getComponentType(), 1);
            Array.set(array, 0, value);
            value = array;
        }
        assertAttributeType(attributeName, value, expectedType);
        return (T) value;
    }
    
    private void assertAttributePresence(String attributeName, Object attributeValue) {
        if (Objects.isNull(attributeValue)) {
            throw new IllegalArgumentException(String.format("在注解 [%s] 的属性中找不到属性 '%s'", attributeName, this.displayName));
        }
    }
    
    private void assertNotException(String attributeName, Object attributeValue) {
        if (attributeValue instanceof Throwable) {
            throw new IllegalArgumentException(String.format(
                    "注解 [%s] 的属性 '%s' 由于异常 [%s] 而无法解析",
                    this.displayName, attributeName, attributeValue), (Throwable) attributeValue);
        }
    }
    
    private void assertAttributeType(String attributeName, Object attributeValue, @NotNull Class<?> expectedType) {
        if (!expectedType.isInstance(attributeValue)) {
            throw new IllegalArgumentException(String.format(
                    "注解 [%s] 的属性 '%s' 类型为 %s，但需要的类型是：%s",
                    this.displayName, attributeName, attributeValue.getClass().getSimpleName(), expectedType.getSimpleName()));
        }
    }
    
    @Override
    public String toString() {
        Iterator<Map.Entry<String, Object>> entries = entrySet().iterator();
        StringBuilder sb = new StringBuilder("{");
        while (entries.hasNext()) {
            Map.Entry<String, Object> entry = entries.next();
            sb.append(entry.getKey());
            sb.append('=');
            sb.append(valueToString(entry.getValue()));
            if (entries.hasNext()) {
                sb.append(", ");
            }
        }
        sb.append('}');
        return sb.toString();
    }
    
    private String valueToString(Object value) {
        if (value == this) {
            return "(this Map)";
        }
        if (value instanceof Object[]) {
            return "[" + StrJoiner.of(",").append((Object[]) value) + "]";
        }
        return String.valueOf(value);
    }
}
